#include "cnetwork.h"
#include <QThread>

static unsigned char secondEncryBuffer[256]= {0};
static unsigned char secondDecryBuffer[256]= {0};

bool CNetwork::secondEncry = true;

CNetwork::CNetwork(QObject *parent) :
    QObject(parent)
{
    _protocol = new TTGIpsProtocol();
    ipBoardcast = QHostAddress(MULTICAST_ADDRESS);
    ipDev = QHostAddress(MULTICAST_ADDRESS);

    if(udpRcv.bind(QHostAddress("0.0.0.0"),NETWORK_PC_PORT))
    {
        connect(&udpRcv,SIGNAL(readyRead()),this,SLOT(readDataSocket()));
    }
    else
    {
        qDebug() << "bind port fail: " << NETWORK_PC_PORT ;
    }
}

CNetwork::~CNetwork()
{
    udpRcv.close();
}

void CNetwork::print_data(char *title,char *src, _int32 len)
{
    _u8 *data = (_u8 *)src;
    _int32 n = 1;
    qDebug("- %s -",title);
    while (n<=len) {
        qDebug(" %02x %02x %02x %02x %02x %02x %02x %02x",
               *data,
               (n+1<=len)?*(data+1):0,
               (n+2<=len)?*(data+2):0,
               (n+3<=len)?*(data+3):0,
               (n+4<=len)?*(data+4):0,
               (n+5<=len)?*(data+5):0,
               (n+6<=len)?*(data+6):0,
               (n+7<=len)?*(data+7):0);

        data  += 8 ;
        n += 8;
    }

    qDebug("- end -");
}

bool CNetwork::ipAddressVaild(QString ip)
{
    return QHostAddress(ip).isNull();
}

void CNetwork::readDataSocket()
{
    QByteArray cmdBytes;

    if (udpRcv.hasPendingDatagrams())
    {
        QHostAddress peerAddr;
        unsigned short port;

        cmdBytes.resize(udpRcv.pendingDatagramSize());
        udpRcv.readDatagram(cmdBytes.data(),cmdBytes.size(),&peerAddr,&port);

        print_data("readDataSocket",cmdBytes.data(),cmdBytes.size());
        if(cmdBytes.at(0)== (char)0xAA && cmdBytes.at(1)== (char)0x55)
        {
            if(secondEncry)
            {
                int len = TTGIpsProtocol::second_round_decry((_u8 *)cmdBytes.data(),cmdBytes.size(),secondDecryBuffer);
                cmdBytes =QByteArray((char *)secondDecryBuffer,len);
            }

            data_analyze(cmdBytes,peerAddr.toString(),port);
        }
    }
}

void CNetwork::udpSendToDev(char *data,quint32 len)
{
    if(secondEncry)
    {
        len = TTGIpsProtocol::second_round_encry((_u8 *)data,len,secondEncryBuffer);
        data = (char *)secondEncryBuffer;
    }

    udpRcv.writeDatagram(data,len,ipDev,devPort);
    udpRcv.flush();
    udpRcv.waitForBytesWritten(100);
}

void CNetwork::udpBordcastSendTo(char *data,quint32 len)
{
    if(secondEncry)
    {
        len = TTGIpsProtocol::second_round_encry((_u8 *)data,len,secondEncryBuffer);
        data = (char *)secondEncryBuffer;
    }

    udpRcv.writeDatagram(data,len,ipBoardcast,NETWORK_DEVICE_PORT);
    udpRcv.flush();
}

void CNetwork::setDevIpAddress(QString ip,int port)
{
    devPort = port;
    ipDev = QHostAddress(ip);
}

void CNetwork::setDevPassword(QString password)
{
    _protocol->setIpsPassword(password);
}

void CNetwork::startBoardCast()
{
    QByteArray data =  _protocol->boardcastPackage();
//    if(secondEncry)
//    {
//        int len = TTGIpsProtocol::second_round_decry((_u8 *)data.data(),data.size(),secondDecryBuffer);
//        data =QByteArray((char *)secondDecryBuffer,len);
//    }
    udpBordcastSendTo(data.data(),data.size());

//    char i[4] = {127,0,0,1};
//    emit boardcastDeviceAck(QByteArray(i,4),QByteArray(i,4));
}

void CNetwork::startSearchIp(QString ip)
{
    _u32 ipNum = QHostAddress(ip).toIPv4Address();
    QByteArray data =  _protocol->searchIpPackage(ipNum);
    udpBordcastSendTo(data.data(),data.size());
}

void CNetwork::devIpChange(QString oldIp,QString newIp,QString pass)
{
    setDevIpAddress(oldIp);
    _u32 newIpNum = QHostAddress(newIp).toIPv4Address();

    _protocol->setIpsPassword(pass);
    QByteArray data = _protocol->devIpChange(newIpNum);

    _ipChangeStatus = UPLOAD_TIMEOUT;
    udpSendToDev(data.data(),data.size());

    checkWaitStatus(_ipChangeStatus);
    emit ipChangeStatus(_ipChangeStatus);

}

int CNetwork::checkWaitStatus(int &status)
{
    int times = 50;
    while(status == UPLOAD_TIMEOUT && times-- >0)
    {
        //processEvents(200);
    }

    return status;
}

void CNetwork::devBinFileUpload(QByteArray data,QString pass)
{
    QByteArray debugData;

    _protocol->setIpsPassword(pass);
    QByteArray handData = _protocol->binFileHand();

    _binUpdateHandStatus = UPLOAD_TIMEOUT;
    udpSendToDev(handData.data(),handData.size());   

    if(checkWaitStatus(_binUpdateHandStatus)!= UPLOAD_SUCCESS)
    {
        emit uploadFileStatus(_binUpdateHandStatus);
        return ;
    }

    int sendSize = 0;
    int size = data.size();
    char *d = data.data();

    _u8 total = (size%UPLOAD_SIZE_EACH) == 0 ? size/UPLOAD_SIZE_EACH : size/UPLOAD_SIZE_EACH + 1;

    for(int i=0;i<total;i++)
    {
        if(i < total - 1 && total>1 )
        {
            sendSize = UPLOAD_SIZE_EACH;
        }
        else
        {
            sendSize = size%UPLOAD_SIZE_EACH;   // last frame
        }

        QByteArray binData = QByteArray(d,sendSize);
        //debugData.append(binData);

        if(i == total -1) // last frame
            binData.append('\0');  // EOF
        else
            d += sendSize;

        _binUpdateStatus = UPLOAD_TIMEOUT;
        QByteArray sendData = _protocol->binFileTansfer(total,i,binData);
        udpSendToDev(sendData.data(),sendData.size());

        if(checkWaitStatus(_binUpdateStatus)!=UPLOAD_SUCCESS)
        {
            emit uploadFileStatus(_binUpdateStatus);
            return ;
        }

    }

    emit uploadFileStatus(_binUpdateStatus);

//    if(data.size() == debugData.size())
//        qDebug() << "file update compare: " << memcmp(data.data(),debugData.data(),data.size());

}

void CNetwork::devPswInit(QString ip,QString pass)
{
    setDevIpAddress(ip);
    QByteArray sendData = _protocol->passwordInitPackage(pass.toLocal8Bit());

    _ipInitStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipInitStatus);
    emit ipInitStatus(_ipInitStatus);

}

QList<QStringList *> * CNetwork::getNumbersSubObjfromList(QStringList *list,unsigned int number)
{
    QList<QStringList *> *wholeList = new QList<QStringList *>();

    QStringList *strList = new QStringList();
    while(list->size()>0)
    {
        strList->append(list->takeFirst());

        if(strList->size() == number)
        {
            wholeList->append(strList);
            strList = new QStringList();
        }
    }

    if(strList->size() > 0)
        wholeList->append(strList);

    return wholeList;
}

void CNetwork::releaseQList(QList<QStringList *> *list)
{
    for(int i=0;i<list->size();i++)
    {
        QStringList *l = list->at(i);
        delete l;
    }

    list->clear();
    delete list;

}

int CNetwork::filterUpdateB2Nphy(QString ip, QString pass, QStringList list, char canPass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(pass);

    short totalSize = (short)list.size();

    QList<QStringList *> *splitList = getNumbersSubObjfromList(&list,500);

    for(int i=0;i<splitList->size();i++)
    {
        _filterStatus = UPLOAD_TIMEOUT;
        QByteArray sendData = _protocol->filterPhyAddrPackage(BUS2NETWORK_PHAYADDR_REQ,splitList->at(i),canPass,totalSize);
        udpSendToDev(sendData.data(),sendData.size());

        checkWaitStatus(_filterStatus);
        if(_filterStatus != BUS2NETWORK_PHAYADDR_SUCCESS)
            break;
    }

    releaseQList(splitList);

    return _filterStatus;
}

int CNetwork::filterUpdateB2Ngroup(QString ip, QString pass, QStringList list, char canPass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(pass);
    short totalSize = (short)list.size();

    QList<QStringList *> *splitList = getNumbersSubObjfromList(&list,500);
    for(int i=0;i<splitList->size();i++)
    {
        _filterStatus = UPLOAD_TIMEOUT;

        QByteArray sendData = _protocol->filterGrpAddrPackage(BUS2NETWORK_GROUPADDR_REQ,
                                                              splitList->at(i),canPass,totalSize);
        udpSendToDev(sendData.data(),sendData.size());

        checkWaitStatus(_filterStatus);
        if(_filterStatus != BUS2NETWORK_GROUPADDR_SUCCESS)
            break;
    }

    releaseQList(splitList);

    return _filterStatus;
}

int CNetwork::filterUpdateN2Bphy(QString ip,QString pass,QStringList list,char canPass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(pass);
    short totalSize = (short)list.size();

    QList<QStringList *> *splitList = getNumbersSubObjfromList(&list,500);
    for(int i=0;i<splitList->size();i++)
    {
        _filterStatus = UPLOAD_TIMEOUT;
        QByteArray sendData = _protocol->filterPhyAddrPackage(NETWORK2BUS_PHAYADDR_REQ,
                                                              splitList->at(i),canPass,totalSize);
        udpSendToDev(sendData.data(),sendData.size());

        checkWaitStatus(_filterStatus);
        if(_filterStatus != NETWORK2BUS_PHAYADDR_SUCCESS)
            break;
    }

    releaseQList(splitList);

    return _filterStatus;
}

int CNetwork::filterUpdateN2Bgroup(QString ip, QString pass, QStringList list, char canPass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(pass);
    short totalSize = (short)list.size();

    QList<QStringList *> *splitList = getNumbersSubObjfromList(&list,500);
    for(int i=0;i<splitList->size();i++)
    {
        _filterStatus = UPLOAD_TIMEOUT;
        QByteArray sendData = _protocol->filterGrpAddrPackage(NETWORK2BUS_GROUPADDR_REQ,
                                                              splitList->at(i),canPass,totalSize);
        udpSendToDev(sendData.data(),sendData.size());

        checkWaitStatus(_filterStatus);
        if(_filterStatus != NETWORK2BUS_GROUPADDR_SUCCESS)
            break;
    }

    releaseQList(splitList);

    return _filterStatus;
}

int CNetwork::filterUpdateN2Bip(QString ip, QString pass, QStringList list, char canPass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(pass);
    short totalSize = (short)list.size();

    QList<QStringList *> *splitList = getNumbersSubObjfromList(&list,500);
    for(int i=0;i<splitList->size();i++)
    {
        _filterStatus = UPLOAD_TIMEOUT;

        QByteArray sendData = _protocol->filterIpPackage(splitList->at(i),
                                                         canPass,totalSize);
        udpSendToDev(sendData.data(),sendData.size());

        checkWaitStatus(_filterStatus);
        if(_filterStatus != NETWORK2BUS_IPADDR_SUCCESS)
            break;
    }

    releaseQList(splitList);

    return _filterStatus;
}

int CNetwork::devPasswordChange(QString ip, QString oldPsw, QString newPsw)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(oldPsw);

    QByteArray sendData = _protocol->devPassChangePackage(newPsw.toLocal8Bit());

    _passChangeStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_passChangeStatus);
    return _passChangeStatus;
}

int CNetwork::iPadSetUser(int cmd, QString ip, QString psw, QString user)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devSetIpadUser(cmd,user.toLocal8Bit());

    _ipadChangeStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadChangeStatus);
    return _ipadChangeStatus;
}

int CNetwork::iPadSetPass(int cmd, QString ip, QString psw, QString pass)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devSetIpadPass(cmd,pass.toLocal8Bit());

    _ipadChangeStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadChangeStatus);
    return _ipadChangeStatus;
}

int CNetwork::iPadSetPort(int cmd, QString ip, QString psw, QString port)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devSetIpadPort(cmd,port);

    _ipadChangeStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadChangeStatus);
    return _ipadChangeStatus;
}

int CNetwork::iPadSetIp(QString ip,QString psw,QString ipadIp)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devSetIpadIp(ipadIp);

    _ipadChangeIPstatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadChangeIPstatus);
    return _ipadChangeIPstatus;
}

int CNetwork::iPadSetNetwork(QString ip, QString psw,QByteArray mac,unsigned int mask,unsigned int gateway)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devSetIpadNetwork(mac,mask,gateway);

    _ipadChangeNetworkStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadChangeNetworkStatus);
    return _ipadChangeNetworkStatus;
}

int CNetwork::iPadReadNetwork(QString ip, QString psw)
{
    setDevIpAddress(ip);
    _protocol->setIpsPassword(psw);

    QByteArray sendData = _protocol->devReadIpadNetwork();

    _ipadReadNetworkStatus = UPLOAD_TIMEOUT;
    udpSendToDev(sendData.data(),sendData.size());

    checkWaitStatus(_ipadReadNetworkStatus);
    return _ipadReadNetworkStatus;
}

void CNetwork::data_analyze(QByteArray bytes,QString peerIP,unsigned short peerPort)
{
    _u16 recv_cmd = _protocol->getCmdCode(bytes);

    switch (recv_cmd) {
        case CMD_DEVICE_STANDBY:{
            QByteArray pass = _protocol->decryPassword(bytes);
            emit boardcastDeviceAck(peerIP,pass);

            break;
        }
        case CMD_IP_USED_ACK:{
            QByteArray pass = " " ; //_protocol->decryPassword(bytes);
            //使用同一个信号
            emit boardcastDeviceAck(peerIP,pass);

            break;
        }
        case CMD_IP_CHANGE_ACK_SUCCESS:
        case CMD_IP_CHANGE_ACK_FAIL:
        {
            _ipChangeStatus = recv_cmd;
            break;
        }
        case CMD_UPLOAD_HAND_ACK:
        {
            _binUpdateHandStatus = UPLOAD_SUCCESS;
            break;
        }
        case CMD_UPLOAD_ACK:
        case CMD_UPLOAD_SUCCESS:
        {
            _binUpdateStatus = UPLOAD_SUCCESS;
            break;
        }
        case CMD_PASS_INIT_SUCCESS:
        case CMD_PASS_INIT_FAIL:
        {
            _ipInitStatus = recv_cmd;
            break;
        }

        case BUS2NETWORK_PHAYADDR_FAIL:
        case BUS2NETWORK_PHAYADDR_SUCCESS:
        case BUS2NETWORK_GROUPADDR_SUCCESS:
        case BUS2NETWORK_GROUPADDR_FAIL:
        case NETWORK2BUS_PHAYADDR_SUCCESS:
        case NETWORK2BUS_PHAYADDR_FAIL:
        case NETWORK2BUS_GROUPADDR_SUCCESS:
        case NETWORK2BUS_GROUPADDR_FAIL:
        case NETWORK2BUS_IPADDR_SUCCESS :
        case NETWORK2BUS_IPADDR_FAIL:
        {
            _filterStatus = recv_cmd;
            break;
        }
        case CMD_PASS_CHANGE_SUCCESS:
        case CMD_PASS_CHANGE_FAIL:
        {
            _passChangeStatus = recv_cmd;
            break;
        }
        case CMD_IPAD_USER_CHANGE_FAIL:
        case CMD_IPAD_USER_CHANGE_SUCCESS:
        case CMD_IPAD_PASS_CHANGE_SUCCESS:
        case CMD_IPAD_PASS_CHANGE_FAIL:
        case CMD_IPAD_PORT_CHANGE_SUCCESS:
        case CMD_IPAD_PORT_CHANGE_FAIL:
        case CMD_IPAD_USER_CHANGE_8A_FAIL:
        case CMD_IPAD_USER_CHANGE_8A_SUCCESS:
        case CMD_IPAD_PASS_CHANGE_8A_SUCCESS:
        case CMD_IPAD_PASS_CHANGE_8A_FAIL:
        case CMD_IPAD_PORT_CHANGE_8A_SUCCESS:
        case CMD_IPAD_PORT_CHANGE_8A_FAIL:
        {
            _ipadChangeStatus = recv_cmd;
            break;
        }
        case CMD_IPAD_IP_CHANGE_FAIL:
        case CMD_IPAD_IP_CHANGE_SUCCESS:
        {
            _ipadChangeIPstatus = recv_cmd;
            break;
        }
        case CMD_IPAD_NETWORK_SUCCESS:
        case CMD_IPAD_NETWORK_FAIL:
        {
            _ipadChangeNetworkStatus = recv_cmd;
            break;
        }
        case 0xEB04:
        {
            _ipadReadNetworkStatus = recv_cmd;

            QByteArray mac = QByteArray(bytes.data()+11,6);
            QByteArray mask = QByteArray(bytes.data()+17,4);
            QByteArray gateway = QByteArray(bytes.data()+21,4);

            emit ipadNetworkInfo(mac,mask,gateway);
            break;
        }
    default:
        break;
    }
}

